package judesart.app.repository;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;

import judesart.app.domain.User;
import judesart.app.domain.UserRole;
import judesart.app.shared.ValidationError;
import judesart.app.shared.ValidationException;
import judesart.app.validation.AppConstraintValidator;
import judesart.app.validation.AppValidator;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Repository
public class UserDAOImpl implements UserDAO
{
    private final static Log LOGGER = LogFactory.getLog(UserDAOImpl.class);

    @PersistenceContext
    private EntityManager entityManager;

    @Inject
    private AppValidator validator;

    private final CheckUniqueValidator checkUniqueValidator = new CheckUniqueValidator();

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public boolean deleteAllUsers()
    {
        entityManager.createQuery("delete from User").executeUpdate();
        entityManager.flush();
        return true;
    }

    @Override
    public User findUserById(Long id)
    {
        return entityManager.find(User.class, id);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public User findUserByUsername(String username)
    {
        try
        {
            User user = (User) entityManager.createNamedQuery(
                    "User.findByUsername").setParameter("username", username).getSingleResult();
            loadDependencies(user);
            return user;
        } catch (NoResultException e)
        {
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public User createUser(User user) throws ValidationException
    {
        validator.validate(user, checkUniqueValidator);
        entityManager.persist(user);
        entityManager.flush();
        return user;
    }

    @SuppressWarnings("unchecked")
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public User updateUser(User user) throws ValidationException
    {
        validator.validate(user, checkUniqueValidator);
        LOGGER.debug("> updateUser : userRoles.size() : "
                + user.getUserRoles().size());
        user = entityManager.merge(user);
        entityManager.flush();
        LOGGER.debug("< updateUser : userRoles.size() : "
                + user.getUserRoles().size());
        return user;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public boolean deleteUser(Long userId)
    {
        entityManager.remove(entityManager.find(User.class, userId));
        return true;
    }

    @SuppressWarnings("unchecked")
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public List<User> findAllUsers()
    {
        List<User> result = entityManager.createNamedQuery("User.findAll").getResultList();
        for (User user : result)
        {
            loadDependencies(user);
        }
        return result;
    }
    
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public User findFeatured() {
    	return (User) entityManager.createNamedQuery("User.findFeatured").setMaxResults(1).getResultList().get(0);
    }

    private void loadDependencies(User user)
    {
        Set<UserRole> userRoles = user.getUserRoles();
        for (UserRole userRole : userRoles)
        {
            userRole.getRole();
            userRole.getUser();
        }
    }

    class CheckUniqueValidator implements AppConstraintValidator<User>
    {
        @Override
        public List<ValidationError> validate(User model)
        {
            List<ValidationError> errors = new ArrayList<ValidationError>();
            User otherUser = findUserByUsername(model.getUsername());
            if (otherUser != null && !otherUser.getId().equals(model.getId()))
            {
                ValidationError error = new ValidationError();
                error.setFieldPath("username");
                error.setMessage("Username already in use.");
                errors.add(error);
            }
            return errors;
        }
    }
}
